﻿namespace Pe.Petecos.Comercial.Cross.Document.Excel
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using DocumentFormat.OpenXml.Spreadsheet;

    /// <summary>
    /// Objeto que representa una hoja de excel
    /// </summary>
    /// <remarks>
    /// Creacion: 	GMD 20140514 <br />
    /// </remarks>
    public class WorkSheet
    {
        /// <summary>
        /// Identificador de hoja
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// Indice de la hoja
        /// </summary>
        public int WorkSheetIndex { get; set; }
        /// <summary>
        /// Nombre
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Título
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// Auto ajustar
        /// </summary>
        public bool AutoFit { get; set; }
        /// <summary>
        /// File de cabecera
        /// </summary>
        public RowData HeaderRow { get; set; }
        /// <summary>
        /// Filas de la hoja
        /// </summary>
        public List<RowData> Rows { get; set; }
        /// <summary>
        /// Indice de inicio de  filas
        /// </summary>
        public UInt32 StartRowIndex { get; set; }
        /// <summary>
        /// Columnas de la hoja
        /// </summary>
        public Columns Columns { get; set; }
        /// <summary>
        /// Valores de definición
        /// </summary>
        public List<DefinedNameVal> DefinedNameValues { get; set; }
        /// <summary>
        /// Constructor por Defecto de implementación de la clase.
        /// </summary>
        public WorkSheet()
        {
            this.StartRowIndex = 1;
            this.Rows = new List<RowData>();
        }
        /// <summary>
        /// Retorna la Lista de Filas Mapeadas
        /// </summary>
        /// <typeparam name="T">Tipo de Parámetro</typeparam>
        /// <param name="listObject">Lista de Objetos</param>
        /// <param name="mapper">Asignador.</param>
        public void MapperListToRows(List<object> listObject, Dictionary<int, string> mapper)
        {
            MapperListToRows(listObject, mapper, null);
        }
        /// <summary>
        /// Retorna la Lista de Filas Mapeadas
        /// </summary>
        /// <typeparam name="T">Tipo de Parámetro</typeparam>
        /// <param name="listObject">Lista de Objetos</param>
        /// <param name="mapper">Asignador</param>
        /// <param name="agrupador">Agrupamiento</param>
        public void MapperListToRows(List<object> listObject, Dictionary<int, string> mapper, string agrupador)
        {
            if (listObject == null || mapper == null)
            {
                throw new ArgumentException();
            }

            this.Rows = new List<RowData>();
            if (listObject.Count > 0)
            {

                var rowIndex = this.StartRowIndex;

                if (this.Title != null && !this.Title.Equals(""))
                {
                    rowIndex++;
                }
                if (this.HeaderRow != null)
                {
                    this.HeaderRow.RowIndex = rowIndex++;
                }

                foreach (var registro in listObject)
                {
                    int valor = (agrupador != null && GetPropertyValue(registro.GetType(), agrupador, registro) != null) ? 1 : 0;
                    RowData row = valor == 0 ? new RowData(rowIndex) : new RowData { RowIndex = rowIndex, OutlineLevel = 1 };
                    row.Cells = MapperToCells(mapper, rowIndex, registro);
                    this.Rows.Add(row);
                    rowIndex++;
                }
            }
        }
        /// <summary>
        /// Permite insertar filas desde un array
        /// </summary>
        /// <param name="datos">datos para poblar las filas</param>
        public void setDataString(List<List<string>> datos)
        {

            this.Rows = new List<RowData>();
            if (datos.Count > 0)
            {

                var rowIndex = this.StartRowIndex;

                if (this.Title != null && !this.Title.Equals(""))
                {
                    rowIndex++;
                }
                if (this.HeaderRow != null)
                {
                    this.HeaderRow.RowIndex = rowIndex++;
                }

                foreach (var registro in datos)
                {
                    RowData row = new RowData(rowIndex);
                    row.SetValues(registro.ToArray());
                    this.Rows.Add(row);
                    rowIndex++;
                }
            }
        }
        /// <summary>
        /// Permite mapear los valores de un objeto a celdas
        /// </summary>
        /// <typeparam name="T">Tipado del objeto</typeparam>
        /// <param name="mapper">Configuración del mapping</param>
        /// <param name="rowIndex">Indice de la fila</param>
        /// <param name="data">Objecto de datos</param>
        /// <returns>Listado de celdas</returns>
        //public List<CellData> MapperToCells<T>(Dictionary<int, string> mapper, uint rowIndex, T data)
        //{
        //    Type type = data.GetType();
        //    var cells = new List<CellData>();
        //    int newIndex = 0;
        //    foreach (var item in mapper)
        //    {
        //        object value = GetPropertyValue(type, item.Value, data);
        //        Type propertyType = type.GetProperty(item.Value).PropertyType;
        //        CellData cell;
        //        if ((propertyType.IsArray || propertyType.Namespace.ToLower().Equals("system.collections.generic")) && value != null)
        //        {
        //            foreach (var objectItem in (value as IEnumerable<object>))
        //            {
        //                cell = new CellData(item.Key + newIndex++, rowIndex) { Value = objectItem != null ? objectItem.ToString() : null };
        //                cell.DataType = "System.String";
        //                if (value != null) { cell.DataType = value.GetType().FullName; }
        //                cells.Add(cell);
        //            }
        //        }
        //        else
        //        {
        //            cell = new CellData(item.Key + newIndex, rowIndex) { Value = value != null ? value.ToString() : null };
        //            cell.DataType = "System.String";
        //            if (value != null) { cell.DataType = value.GetType().FullName; }
        //            cells.Add(cell);
        //        }
        //    }
        //    return cells;
        //}
        /// <summary>
        /// Permite mapear los valores de un objeto a celdas
        /// </summary>
        /// <typeparam name="T">Tipado del objeto</typeparam>
        /// <param name="mapper">Configuración del mapping</param>
        /// <param name="rowIndex">Indice de la fila</param>
        /// <param name="data">Objecto de datos</param>
        /// <returns>Listado de celdas</returns>
        public List<CellData> MapperToCells(Dictionary<int, string> mapper, uint rowIndex, object data)
        {
            Type type = data.GetType();
            var cells = new List<CellData>();
            int newIndex = 0;
            foreach (var item in mapper)
            {
                object value = GetPropertyValue(type, item.Value, data);
                Type propertyType = type.GetProperty(item.Value).PropertyType;
                CellData cell;
                if ((propertyType.IsArray || propertyType.Namespace.ToLower().Equals("system.collections.generic")) && value != null)
                {
                    foreach (var objectItem in (value as IEnumerable<object>))
                    {
                        cell = new CellData(item.Key + newIndex++, rowIndex) { Value = objectItem != null ? objectItem.ToString() : null };
                        cell.DataType = "System.String";
                        if (value != null) { cell.DataType = value.GetType().FullName; }
                        cells.Add(cell);
                    }
                }
                else
                {
                    if (value is bool)
                    {
                        bool? boolValue = value as bool?;
                        value = boolValue.HasValue && boolValue.Value ? "✔" : "";
                    }
                    cell = new CellData(item.Key + newIndex, rowIndex) { Value = value != null ? value.ToString() : null };
                    cell.DataType = "System.String";
                    if (value != null) { cell.DataType = value.GetType().FullName; }
                    cells.Add(cell);
                }
            }
            return cells;
        }
        /// <summary>
        /// Permite obtener el valor de una propiedad de un objecto
        /// </summary>
        /// <param name="type">Tipo</param>
        /// <param name="propertyName">Nombre de Propiedad</param>
        /// <param name="item">Item</param>
        /// <returns>Objeto</returns>

        public object GetPropertyValue(Type type, string propertyName, object item)
        {
            PropertyInfo property = type.GetProperty(propertyName);
            if (property == null)
            {
                throw new ArgumentException("No se encontro la propiedad mapeada, propiedad invalida :" + propertyName);
            }
            object value = property.GetValue(item, null);
            return value;
        }
    }
}
